package com.swak.fx.support.ui;

import com.swak.utils.StringUtils;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.Skin;
import javafx.scene.control.TextArea;
import javafx.scene.control.skin.TextAreaSkin;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;

public class UITextArea extends TextArea implements UILifecycle {

	private static final PseudoClass HAS_WORD_LIMIT = PseudoClass.getPseudoClass("word-limit-visible"); //$NON-NLS-1$
	// ================================================================================
	// Properties
	// ================================================================================
	private final String STYLE_CLASS = "ui-text-area";
	private final String STYLESHEET = UI.load("css/UITextArea.css");

	public UITextArea() {
		super();
		init();
	}

	public UITextArea(String text) {
		super(text);
		init();
	}

	public UITextArea(String text, String promptText) {
		super(text);
		setPromptText(promptText);
		init();
	}

	@Override
	public void initializeParts() {
		getStyleClass().add(STYLE_CLASS);
		setWrapText(true);
		setPrefHeight(120);
	}

	@Override
	public void layoutParts() {
	}

	@Override
	public void setupValueChangedListeners() {

	}

	public UITextArea registerEntryEventHandler(Runnable run) {
		this.addEventHandler(KeyEvent.KEY_TYPED, (e) -> {
			int code = e.getCharacter().hashCode();
			if (code == 13) {
				run.run();
			}
		});
		return this;
	}

	// ================================================================================
	// Overridden Methods
	// ================================================================================

	@Override
	protected Skin<?> createDefaultSkin() {
		return new UITextAreaSkin(this);
	}

	@Override
	public String getUserAgentStylesheet() {
		return STYLESHEET;
	}

	// ================================================================================
	// Append
	// ================================================================================
	private BooleanProperty wordLimitProperty = new SimpleBooleanProperty(false);
	private IntegerProperty maxWordProperty = new SimpleIntegerProperty();

	public IntegerProperty maxWordProperty() {
		return maxWordProperty;
	}

	public UITextArea MaxWord(int words) {
		maxWordProperty.set(words);
		return this;
	}

	public int MaxWord() {
		return maxWordProperty.get();
	}

	public BooleanProperty wordLimitProperty() {
		return wordLimitProperty;
	}

	public UITextArea WordLimit() {
		wordLimitProperty.set(true);
		return this;
	}

	// ================================================================================
	// Skin
	// ================================================================================

	public static class UITextAreaSkin extends TextAreaSkin {

		UITextArea control;
		StackPane pane;
		Label label;

		public UITextAreaSkin(UITextArea textArea) {
			super(textArea);
			this.control = textArea;

			updateChildren();
			registerChangeListener(control.wordLimitProperty(), e -> updateChildren());
			registerChangeListener(control.textProperty(), e -> updateLimit());
			registerChangeListener(control.maxWordProperty(), e -> updateLimit());
		}

		private void updateChildren() {
			getChildren().remove(pane);
			boolean show = control.wordLimitProperty().get();
			if (show) {
				Label label = new Label();
				label.getStyleClass().add("word-limit-text");
				StackPane pane = new StackPane(label);
				pane.setManaged(false);
				pane.setAlignment(Pos.CENTER);
				pane.getStyleClass().add("word-limit");
				getChildren().add(pane);
				this.label = label;
				this.pane = pane;
			} else {
				this.label = null;
				this.pane = null;
			}

			control.pseudoClassStateChanged(HAS_WORD_LIMIT, this.pane != null);
		}

		private void updateLimit() {
			int maxWord = control.MaxWord();
			String text = control.getText();
			int length = StringUtils.length(text);
			if (this.label != null) {
				if (maxWord <= 0) {
					this.label.setText(StringUtils.format("%s 字", length));
				} else {
					this.label.setText(StringUtils.format("%s / %s", length, maxWord));
				}
			}

			// 限制最大
			if (maxWord > 0 && maxWord < length) {
				control.textProperty().set(text.substring(0, maxWord));
			}
		}
		
		private void layoutGraphic(double fullHeight, double w) {
			final double rightWidth = pane == null ? 0.0 : snapSizeX(pane.prefWidth(fullHeight));
			final double rightHeight = pane == null ? 0.0 : snapSizeY(pane.prefHeight(rightWidth));
			final double rightStartX = pane == null ? 0.0 : w - rightWidth + snappedLeftInset();
			pane.resizeRelocate(rightStartX - 22, fullHeight - rightHeight - 5, rightWidth, rightHeight);
		}

		@Override
		protected void layoutChildren(double x, double y, double w, double h) {
			final double fullHeight = h + snappedTopInset() + snappedBottomInset();
			super.layoutChildren(x, y, w, h);
			if (pane != null) {
				this.layoutGraphic(fullHeight, w);
			}
		}
	}
}
